
<!DOCTYPE html>
<html lang="zh-cn">
    
<head>
    <meta http-equiv="Content-Type" content="text/html" charset="UTF-8" >
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="generator" content="XGHeaven&#39;s Blog">
    <title>归档: 2018 - XGHeaven&#39;s Blog</title>
    <meta name="author" content="XGHeaven">
    
    
    
        <link rel="alternate" type="application/atom+xml" title="RSS" href="/atom.xml">
    
    <meta property="og:type" content="blog">
<meta property="og:title" content="XGHeaven&#39;s Blog">
<meta property="og:url" content="http://blog.xgheaven.cn/archives/2018/index.html">
<meta property="og:site_name" content="XGHeaven&#39;s Blog">
<meta property="og:locale" content="zh-cn">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="XGHeaven&#39;s Blog">
<meta name="twitter:creator" content="@XGHeaven">
    
    
        
    
    
        <meta property="og:image" content="https://www.gravatar.com/avatar/f34474afe04544ffbe944a804f0ae762?s=640"/>
    
    
    
    
    <!--STYLES-->
    <link rel="stylesheet" href="/assets/css/style-mhhgzztqkaub4zd4cl8bd83f7mgh9j6njnhilft4hamhrjsliqyzwo2cfzdk.min.css">
    <!--STYLES END-->
    
    <script type="text/javascript">
        var _gaq = _gaq || [];
        _gaq.push(['_setAccount', 'UA-71388235-1']);
        _gaq.push(['_trackPageview']);
        (function() {
            var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
            ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
            var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
        })();
    </script>

    
    <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?9894361f828b64144ac1f2ac0c58c300";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
    </script>

</head>

    <body>
        <div id="blog">
            <!-- Define author's picture -->


    
        
    

<header id="header" data-behavior="1">
    <i id="btn-open-sidebar" class="fa fa-lg fa-bars"></i>
    <h1 class="header-title">
        <a class="header-title-link" href="/ ">XGHeaven&#39;s Blog</a>
    </h1>
    
        
            <a  class="header-right-picture "
                href="#about">
        
        
            <img class="header-picture" src="https://www.gravatar.com/avatar/f34474afe04544ffbe944a804f0ae762?s=90"/>
        
        </a>
    
</header>
            <!-- Define author's picture -->


    

<nav id="sidebar" data-behavior="1">
    
        <div class="sidebar-profile">
            <a href="/#about">
                    <img class="sidebar-profile-picture" src="https://www.gravatar.com/avatar/f34474afe04544ffbe944a804f0ae762?s=110"/>
            </a>
            <span class="sidebar-profile-name">XGHeaven</span>
        </div>
    
    
        <ul class="sidebar-buttons">
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link "
                         href="/ "
                        
                    >
                
                    <i class="sidebar-button-icon fa fa-lg fa-home"></i>
                    <span class="sidebar-button-desc">首页</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link "
                         href="/all-categories"
                        
                    >
                
                    <i class="sidebar-button-icon fa fa-lg fa-bookmark"></i>
                    <span class="sidebar-button-desc">分类</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link "
                         href="/all-tags"
                        
                    >
                
                    <i class="sidebar-button-icon fa fa-lg fa-tags"></i>
                    <span class="sidebar-button-desc">标签</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link "
                         href="/all-archives"
                        
                    >
                
                    <i class="sidebar-button-icon fa fa-lg fa-archive"></i>
                    <span class="sidebar-button-desc">归档</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link "
                         href="#about"
                        
                    >
                
                    <i class="sidebar-button-icon fa fa-lg fa-question"></i>
                    <span class="sidebar-button-desc">关于</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link "
                         href="/friend-link"
                        
                    >
                
                    <i class="sidebar-button-icon fa fa-lg fa-link"></i>
                    <span class="sidebar-button-desc">友情链接</span>
                </a>
        </li>
        
    </ul>
    
        <ul class="sidebar-buttons">
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link " href="https://github.com/XGHeaven" target="_blank">
                
                    <i class="sidebar-button-icon fa fa-lg fa-github"></i>
                    <span class="sidebar-button-desc">GitHub</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link " href="https://twitter.com/xgheaven" target="_blank">
                
                    <i class="sidebar-button-icon fa fa-lg fa-twitter"></i>
                    <span class="sidebar-button-desc">Twitter</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link " href="https://facebook.com/xgheaven" target="_blank">
                
                    <i class="sidebar-button-icon fa fa-lg fa-facebook"></i>
                    <span class="sidebar-button-desc">Facebook</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link " href="https://www.linkedin.com/xgheaven/" target="_blank">
                
                    <i class="sidebar-button-icon fa fa-lg fa-linkedin"></i>
                    <span class="sidebar-button-desc">LinkedIn</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link " href="mailto:xgheaven@gmail.com" target="_blank">
                
                    <i class="sidebar-button-icon fa fa-lg fa-envelope-o"></i>
                    <span class="sidebar-button-desc">邮箱</span>
                </a>
        </li>
        
    </ul>
    
        <ul class="sidebar-buttons">
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link "
                         href="/atom.xml"
                        
                    >
                
                    <i class="sidebar-button-icon fa fa-lg fa-rss"></i>
                    <span class="sidebar-button-desc">RSS</span>
                </a>
        </li>
        
            <li class="sidebar-button">
                
                    <a  class="sidebar-button-link "
                         href="/changelog"
                        
                    >
                
                    <i class="sidebar-button-icon fa fa-lg fa-line-chart"></i>
                    <span class="sidebar-button-desc">Changelog</span>
                </a>
        </li>
        
    </ul>
    
</nav>

            
            <div id="main" data-behavior="1"
                 class="
                        hasCoverMetaIn
                        ">
                
    

<section class="postShorten-group main-content-wrap">
    
    
    <article class="postShorten postShorten--thumbnailimg-bottom" itemscope itemType="http://schema.org/BlogPosting">
        <div class="postShorten-wrap">
            <div class="postShorten-header">
                <h1 class="postShorten-title" itemprop="headline">
                    
                        <a class="link-unstyled" href="/2018/05/03/npm-to-yarn-to-npm/">
                            为什么我从 Npm 到 Yarn 再到 Npm?
                        </a>
                    
                </h1>
                <div class="postShorten-meta">
    <time itemprop="datePublished" content="Thu May 03 2018 17:52:35 GMT+0800">
	
		    5月 03, 2018
    	
    </time>
    
        <span>发布在 </span>
        
    <a class="category-link" href="/categories/Node-js/">Node.js</a>


    
</div>
            </div>
            
            
                <div class="postShorten-content" itemprop="articleBody">
                    <blockquote>
<p>从接触到 node 环境来说，其中一个不可或缺的一部分便是 npm 包管理，但是由于官方的 npm 有各种各样的问题，于是催生了很多不同的版本，这其中的曲折也许只有过来人才知道。</p>
</blockquote>
<h2 id="放弃-npm"><a href="#放弃-npm" class="headerlink" title="放弃 npm?"></a>放弃 npm?</h2><h3 id="上古时代"><a href="#上古时代" class="headerlink" title="上古时代"></a>上古时代</h3><p>在上古版本(应该是 npm3 以前的版本，具体我也记不清了)，npm 的安装策略并不是扁平化的，也就是说比如你安装一个 <code>express</code>，那么你会在 <code>node_modules</code> 下面只找到一个 <code>express</code> 的文件夹。而 <code>express</code> 依赖的项目都放在其文件夹下。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">- app/</div><div class="line">  - package.json</div><div class="line">  - node_modules/</div><div class="line">    - express/</div><div class="line">      - index.js</div><div class="line">      - package.json</div><div class="line">      - node_modules/</div><div class="line">        - ...</div></pre></td></tr></table></figure>
<p>这个带来的问题或许 windows 用户深谙其痛，因为在这种安装环境下，会导致目录的层级特别高，而对于 windows 来说，最大的路径长度限制在 248 个字符(<a href="https://www.cnblogs.com/52cik/p/node-modules-del.html" title="Windows 路径长度" target="_blank" rel="external">更多请见此</a>)，再加上 <code>node_modules</code> 这个单词又特别长，所以你懂得，哈哈哈。解决方案啥的自己去搜索吧，反正估计现在也没人会用上古版本了。</p>
<p>除了 windows 用户出现的问题以外，还有一个更严重的问题，就是模块都是独立的，比如说位于 <code>express</code> 下面的 <code>path-to-regexp</code> 和 <code>connect</code> 下面的 <code>path-to-regexp</code> 的模块是两个不同的模块。<br>那么这个会带来什么影响呢？其实在使用上，并没有什么太大的影响，但是内存占用过大。因为很多相同模块位于不同模块下面就会导致有多个实例的出现(为什么会加载多个实例，请查看 <a href="https://github.com/nodejs/node/blob/master/lib/internal/modules/cjs/loader.js" title="Node 模块加载" target="_blank" rel="external">Node 模块加载</a>)。你想想，都是同样的功能，为什么要实例这么多次呢？不能就加载一次，复用实例么？</p>
<p>上古时代的 npm 的缺点可以说还是很多的：</p>
<ul>
<li>目录嵌套层级过深</li>
<li>模块实例无法共享</li>
<li>安装速度很慢，这其中有目录嵌套的原因，也有安装逻辑的问题。因为 npm 是请求完一个模块之后再去请求另一个模块，这就会导致同一个时刻，只有一个模块在下载、解析、安装。</li>
</ul>
<h3 id="软链时代"><a href="#软链时代" class="headerlink" title="软链时代"></a>软链时代</h3><p>后面，有人为了解决目录嵌套层次过高的问题，引入的软链接的方案。</p>
<p>简单来说，就是将所有的包都扁平化安装到一个位置，然后通过软链接(windows 快捷方式)的方式组合到 <code>node_modules</code> 中。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">- app/</div><div class="line">- node_modules</div><div class="line">  - .modules/</div><div class="line">    - express@x.x.x/</div><div class="line">      - node_modules</div><div class="line">        - connect -&gt; ../../connect@x.x.x</div><div class="line">        - path-to-regexp -&gt; ../../path-to-regexp@x.x.x</div><div class="line">        - ... -&gt; ../../package-name@x.x.x</div><div class="line">    - connect@x.x.x/</div><div class="line">    - path-to-regexp@x.x.x/</div><div class="line">    - ...others</div><div class="line">  - express -&gt; ./.modules/express@x.x.x</div></pre></td></tr></table></figure>
<p>这样做的好处就是可以将整体的逻辑层级简化到很少的几层。而且对于 node 的模块解析来说，可以很好的解决相同模块不同位置导致的加载多个实例，进而导致内存占用的情况。</p>
<p>基于这种方案，有 <a href="https://www.npmjs.com/package/npminstall" target="_blank" rel="external">npminstall</a> 以及 pnpm 这个包实现了这种方案，其中 cnpm 使用的就是 npminstall，不过他们实现的方式和我上面讲的是有差异的，<a href="https://www.npmjs.com/package/npminstall#node_modules-directory" target="_blank" rel="external">具体请看</a>。简单来讲，他们没有 <code>.modules</code> 这一层。更多的内容，请看 npminstall 的 README。</p>
<p>总的来讲这种解决方案有还有以下几个好处：</p>
<ul>
<li>兼容性很好</li>
<li>在保证目录足够简洁的情况下，解决了上面的两个问题（目录嵌套和多实例加载）。</li>
<li>安装速度很快，因为采用了软连接的方式加上多线程请求，多个模块同时下载、解析、安装。</li>
</ul>
<p>那么缺点也是挺致命的：</p>
<ul>
<li>一般情况下都是第三方库实现这个功能，所以无法保证和 npm 完全一致的行为，所以遇到问题只能去找作者提交一下，然后等待修复。</li>
<li>无法和 npm 很方便的一起使用。最好是要么只用 npm，要么只用 cnpm/pnpm，两者混用可能会产生很奇葩的效果。</li>
</ul>
<h2 id="npm3-时代"><a href="#npm3-时代" class="headerlink" title="npm3 时代"></a>npm3 时代</h2><p>最大的改变就是将目录层级从嵌套变到扁平化，可以说很好的解决了上面嵌套层级过深以及实例不共享的问题。但是，npm3 在扁平化方案下，选择的并不是软连接的方式，而是说直接将所有模块都安装到 <code>node_modules</code> 下面。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">- app/</div><div class="line">- node_modules/</div><div class="line">  - express/</div><div class="line">  - connect/</div><div class="line">  - path-to-regexp/</div><div class="line">  - ...</div></pre></td></tr></table></figure>
<p>如果出现了不同版本的依赖，比如说 <code>package-a</code> 依赖 <code>package-c@0.x.x</code> 的版本，而 <code>package-b</code> 依赖 <code>package-c@1.x.x</code> 版本，那么解决方案还是像之前的那种嵌套模式一样。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">- app/</div><div class="line">- node_modules/</div><div class="line">  - package-a/</div><div class="line">  - package-c/</div><div class="line">    - // 0.x.x</div><div class="line">  - package-b/</div><div class="line">    - node_modules/</div><div class="line">      - package-c/</div><div class="line">        - // 1.x.x</div></pre></td></tr></table></figure>
<p>至于那个版本在外面，那个版本在里面，似乎是根据安装的先后顺序有关的，具体的我就不验证了。如果有人知道的话，欢迎告诉我。</p>
<p>在这个版本之后，解决了大部分问题，可以说 npm 跨入了一个新的世界。但是还要一个问题就是，他的安装速度依旧很慢，相比 cnpm 来说。所以他还有很多进步的空间。</p>
<h2 id="yarn-的诞生"><a href="#yarn-的诞生" class="headerlink" title="yarn 的诞生"></a>yarn 的诞生</h2><p>随着 Node 社区的越来越大，也有越来越多的人将 Node 应用到企业级项目。这也让 npm 暴露出很多问题：</p>
<ul>
<li>无法保证两次安装的版本是完全相同的。大家都知道 npm 通过语义化的版本号安装应用，你可以限制你安装模块的版本号，但是你无法限制你安装模块依赖的模块的版本号。即使有 shrinkwrap 的存在，但是很少有人会用。</li>
<li>安装速度慢。上文已经讲过，在一些大的项目当中，可能依赖了上千个包，甚至还包括了 C++ Addon，严重的话，安装可能要耗时 10 分钟甚至到达半个小时。这很明显是无法忍受的，尤其是配合上 CI/CD。</li>
<li>默认情况下，npm 是不支持离线模式的，但是在有些情况下，公司的网络可能不支持连接外网，这个时候利用缓存构建应用就是很方便的一件事情。而且可以大大减少网络请求。</li>
</ul>
<p>所以，此时 yarn 诞生了，为的就是解决上面几个问题。</p>
<ul>
<li>引入 yarn.lock 文件来管理依赖版本问题，保证每次安装都是一致的。</li>
<li>缓存加并行下载保证了安装速度</li>
</ul>
<p>那个时候我还在使用 cnpm，我特地比较了一下，发现还是 cnpm 比较快，于是我还是继续使用着 cnpm，因为对于我来说足够了。但是后面发现 yarn 真的越来越火，再加上 cnpm 长久不更新。我也尝试着去了用 yarn，在尝试之后，我彻底放弃了 cnpm。而且直到现在，似乎还没有加入 lock 的功能。</p>
<p>当然 yarn 还不只只有这么几个好处，在用户使用方面：</p>
<ul>
<li>提供了非常简洁的命令，将相关的命令进行分组，比如说 <code>yarn global</code> 下面都是与全局模块相关的命令。而且提示非常完全，一眼就能看明白是什么意思。不会像 npm 一样，<code>npm --help</code> 就是一坨字符串，还不讲解一下是什么用处，看着头疼。</li>
<li>默认情况安装会保存到 dependencies，不需要像 npm 一样手动添加 <code>-S</code> 参数</li>
<li>非常方便的 yarn run 命令，不仅仅会自动查看 package.json 中 scripts 下面的内容，还是查找 <code>node_modules/.bin</code> 下的可执行文件。这个是我用 yarn 最高的频率。比如你安装了 <code>yarn add mocha</code>，然后就可以通过 <code>yarn run mocha</code> 直接运行 <code>mocha</code>。而不需要 <code>./node_modules/.bin/mocha</code> 运行。是我最喜欢的一个功能</li>
<li>交互式的版本依赖更新。npm 你只能先通过 <code>npm outdated</code> 看看那些包需要更新，然后通过 <code>npm update [packages]</code> 更新指定的包。而在 yarn 当中，可以通过交互式的方式，来选择那些需要更新，那些不需要。</li>
<li>全局模块的管理。npm 管理全局模块的方式是通过直接在 <code>/usr/lib/node_modules</code> 下面安装，然后通过软连接连接到 <code>/usr/local/bin</code> 目录下。而 yarn 的做法是选择一个目录，这个目录就是全局模块安装的地方，然后将所有的全局模块当做一个项目，从而进行管理。这个好处就是，你可以直接备份这个目录当中的 package.json 和 yarn.lock 文件，从而可以很方便的在另一个地方还原你安装了那些全局模块。至于这个目录的问题，通过 <code>yarn global dir</code> 命令就可以找到，mac 下是在 <code>~/.config/yarn/global/</code>，linux 我没有测试过。</li>
</ul>
<p>可以说 yarn 用起来非常舒服，但是唯一的缺点就是不是 npm 官方出的，更新力度、兼容性都会差一些。但这也阻挡不住 yarn 在 Node 社区的火热程度。很快，大家纷纷从 npm 切换到 yarn 上面。</p>
<h2 id="重拾-npm-5"><a href="#重拾-npm-5" class="headerlink" title="重拾 npm 5"></a>重拾 npm 5</h2><p>在受到 yarn 的冲击之后，npm 官方也决定改进这几个缺点，于是发布了和 Yarn 对抗(这个词是我意淫的)的 npm5 版本。</p>
<ol>
<li>引入了 package-lock.json，并且默认就会添加，和 yarn.lock 是一样的作用，并且取代之前的 npm shrinkwrap。</li>
<li>默认情况下，安装会自动添加 dependencies，不需要手动书写 <code>-S</code> 参数</li>
<li>提升了安装速度，和之前有了很大的进步，但是和 yarn 相比，还是略微慢一些</li>
</ol>
<p>至此，yarn 和 npm 的差距已经非常非常小了，更多的差距体现在用户体验层面，我使用 yarn 的功能也只剩下全局模块管理、模块交互式更新和 <code>yarn run</code> 这个命令了。</p>
<p>但是后面推出的 npx 让我放弃了使用 <code>yarn run</code> 这个命令。不是说 npx 比 yarn 有多好，而是说 npm 集成了这个功能，也就没必要再去使用第三方的工具了。而且 npx 还支持临时安装模块，也就是那种只用一次的命令，用完就删掉了。</p>
<p>后面我又发现了 <code>npm-check</code> 这个工具，我用它来替代了 yarn 的交互式更新。</p>
<p>然而 npm6 的出现加入了缓存，并且又进一步提升了速度，可以说直逼 yarn。</p>
<p>于是 yarn 对我来说只剩下一个全局模块管理的功能了。我的整个开发流程以及从 yarn 切换回 npm 上面了。或许后面的日子我也会让 npm 来接管全局模块管理，从而放弃使用 yarn。但是我还是会装 yarn，毕竟有一些老项目还是用 yarn 的。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>我经历了从 npm -&gt; cnpm -&gt; yarn -&gt; (npm + npm-check + npx) 的一个循环，也见证了 npm 社区的一步步发展。而且 yarn 的更新频率也非常慢，可能一个月才更新一次，这也让我逐渐放弃使用 yarn。</p>
<p>有的时候感觉，第三方的终究是第三方，还是没有原生的好用和方便，而且用起来安心。</p>

                    
                        

                    
                    
                        <p>
                            <a href="/2018/05/03/npm-to-yarn-to-npm/#post-footer" class="postShorten-excerpt_link link">
                                注释和共享
                            </a>
                        </p>
                    
                </div>
            
        </div>
        
    </article>
    
    
    <article class="postShorten postShorten--thumbnailimg-bottom" itemscope itemType="http://schema.org/BlogPosting">
        <div class="postShorten-wrap">
            <div class="postShorten-header">
                <h1 class="postShorten-title" itemprop="headline">
                    
                        <a class="link-unstyled" href="/2018/04/16/nest-js-try-first/">
                            Nest.js 入手以及企业化的思考
                        </a>
                    
                </h1>
                <div class="postShorten-meta">
    <time itemprop="datePublished" content="Mon Apr 16 2018 01:18:00 GMT+0800">
	
		    4月 16, 2018
    	
    </time>
    
</div>
            </div>
            
            
                <div class="postShorten-content" itemprop="articleBody">
                    <blockquote>
<p>本人是一名 Node.js 实习生，在进入大搜车之后，有幸见识到 Akyuu.js 这个框架。但是这个框架是使用 Express + Callback 的方式，我不是很喜欢。在我的推荐以及社区的发展下，组长决定用 TS + Async/Await 来试一试。于是我也去了解了一下 TS 的后端框架有哪些，结果经过别人推荐，找到了 Nest.js 这个想法几乎和我一模一样的框架。</p>
</blockquote>
<h2 id="框架简介"><a href="#框架简介" class="headerlink" title="框架简介"></a>框架简介</h2><p>因为我这个不是教程向，所以就不细讲，可以查看 <a href="https://nestjs.com/" target="_blank" rel="external">Nest.js</a> 官网。从我的感性角度来讲，简单说一下以下几个特点：</p>
<ul>
<li>去中心化路由。所有的路由通过装饰器与 Controller 绑定。简单、明了，学习成本低。</li>
<li>TypeScript/Rx.js 加持。智能补全，代码分析，静态类型等等优点。如果你只是个人用用的话，可能会觉得很全。但是放在企业当中使用，是非常大的优点。</li>
<li>依赖注入。从 Angular 那里学习而来，但是进行了一些简化，但是完全够用。比如说简化掉了 deps。</li>
<li>模块思想。Node 社区的后端框架，其实都被 Express 导向到了中间件的模式。而 Nest.js 却从 Angular 当中吸取到了模块的思想。不同的 Service、Controller、Component 组成不同的模块。模块之间可以相互依赖，也可以独立存在，这大大减少了测试和逻辑的复杂度。</li>
<li>易于扩展。以往的框架，你能做的就是编写业务逻辑，而其他的你都很难去做到。于是传统的后端框架不得不引入了一套插件机制来增强框架的扩展性。但是 Nest.js 将插件的功能直接内置到了框架当中。传统的插件在这里可以认为就是一个模块，通过加载不同的模块来添加不同的功能。</li>
<li>Express 基石。有人会说，不是现在 Koa 才是更好的模型么？洋葱模型可以解决更多复杂的问题。没错，我不反对这个言论。但是我想说的是，Express 还是最简单最通用的方式，因为他不赖 Generator/Promise，只需要你又一个 Node.js 运行环境，支持 Callback 就可以了。（话说应该没有不支持 Callback 的 Node.js 环境吧，哈哈哈）不管怎么样，Express 的覆盖面还是比 Koa 要广不少。</li>
<li>条条大路通罗马。那么有人就问了，那我要实现洋葱模型怎么办呢？我想说，办法总是会有的。而在 Nest.js 当中，通过 Interceptor ，可以很好的实现洋葱模型。也就是说你可以通过 Interceptor 来记录请求的耗时。</li>
<li>同步代码。这里所说的同步代码并不是单单指的是 async/await。在很多支持 async/await 的框架中，如果你想返回值，如果是 Express ，你还是需要调用 <code>resp.send(await getValue())</code>，而 koa 也是需要调用 <code>ctx.body = await getValue()</code>。但是在 Nest.js 中，只需要 <code>return await getValue()</code> 即可。实现真正的同步编写业务逻辑代码。</li>
<li>逻辑分层。其实很多功能，都是可以通过中间件来实现的。但是不同类型的功能有不同的需求，如果只是通过中间件来实现，势必会导致有一些重复的代码。于是 Nest.js 里面引入了 Pipe/Interceptor/Guard/ExceptionFilter 等逻辑层。不同的层里面处理相似的事情，比如说 Pipe 处理的是输入数据的转换。而 Interceptor 来实现洋葱模型。Guard 用于权限校验等拦截任务。ExceptionFilter 用来处理错误数据。这种分层带来的好处就是可以让代码更加清晰，主需要思考这个层需要做的事情，而不需要站在中间件的层面去考虑这个事情。</li>
<li>Validation。自带校验，而且和 TS 结合的非常完美，使用起来很舒服，请看<a href="https://docs.nestjs.com/pipes" target="_blank" rel="external">教程</a></li>
<li>输入参数的转换。这个其实是一个很方便的方面。有的时候你需要将输入的参数转换成一个类，这个时候你就可以通过 Validation 进行转换。你要是不想用自动转换，可以通过传统的手动转换的方式。</li>
<li>测试功能完美。由于采用了依赖注入，所以测试简直简单的不得了。而且官方也提供了一系列测试工具，也能很好的解决单元测试的问题。</li>
</ul>
<h2 id="Nest-js-企业化当中的问题"><a href="#Nest-js-企业化当中的问题" class="headerlink" title="Nest.js 企业化当中的问题"></a>Nest.js 企业化当中的问题</h2><ul>
<li>目录无约束。在企业当中，不对目录进行约束会导致代码越来越乱。从而降低了代码可维护性。</li>
<li>没有配置管理功能。在框架开发中，配置往往是一个很重要的功能。比如说配置数据库的连接，配置监听的端口。</li>
<li>没有进程管理。虽然有提供 <code>@nestjs/cli</code>，但是这个提供的仅仅是一个项目的创建的能力。</li>
<li>部分文档讲解不详细，会提高入门的门槛。</li>
</ul>
<p>不过总的来说，前面几点也正是 Nest.js 灵活性的保证。但是我们真正在开发当中，还是需要一种合理的约束来保证开发的统一。</p>
<h2 id="Nest-js-企业化的尝试"><a href="#Nest-js-企业化的尝试" class="headerlink" title="Nest.js 企业化的尝试"></a>Nest.js 企业化的尝试</h2><p>那么我们这里针对上面的几个问题，尝试采用一些方式来进行约束。</p>
<h3 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h3><p>我们对项目指定如下的规则：</p>
<ul>
<li>全部通过 TypeScript 书写，并且全部位于 <code>src</code> 目录下</li>
<li>入口文件是 <code>main.ts</code> 如果没有特殊情况，不动这个文件</li>
<li>配置放在 <code>src/config</code> 文件夹下</li>
<li>所有的 Service/Controller/Logic/Component 等都挂载到 <code>MainModule</code> 下。</li>
<li>其中 <code>module</code> 文件夹存放自定义的 Module，或者说希望独立成模块但是还没有完全独立出来的。其中目录结构和这个项目目录结构类似</li>
<li><code>boot</code> 文件夹是项目启动代码的时候执行的，这部分在 Nest.js 当中没有给出。我这里打算添加这个功能，但是还没有想好具体的实现形式，所以待定。</li>
<li><code>interface</code>/<code>enum</code> 等数据随着对应的 service 导出。不另做说明。比如说 <code>car.service.ts</code> 除了可以导出 <code>CarService</code> 类以外，还可以导出 <code>CarType</code> enum。</li>
<li><code>dest</code> 文件夹是编译之后的文件，可以直接输入 <code>node dest/main.js</code> 运行。</li>
<li>命名规则<ul>
<li>所有的文件除了 main.ts 和类文件以外，都要添加类型后缀，比如说 <code>user.model.ts</code> <code>car.controller.ts</code> <code>google.logic.ts</code>。但是比如说只是一个 <code>Car</code> 类，那么可以直接命名成 <code>car.ts</code></li>
<li>不允许通过 <code>export default</code> 导出数据。一方面是为了方便导入的时候保证命名的统一，另一方面可以随时导出 interface/enum 等内容。</li>
<li>所有的测试文件后缀名都以 <code>.spec.ts</code> 或 <code>.test.ts</code> 结尾。</li>
</ul>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">|-- dest</div><div class="line">    |--- ...</div><div class="line">|-- src</div><div class="line">    |-- config</div><div class="line">    |-- controller</div><div class="line">    |-- model</div><div class="line">    |-- service</div><div class="line">    |-- logic</div><div class="line">    |-- component</div><div class="line">    |-- boot</div><div class="line">    |-- module</div><div class="line">        |-- module-name</div><div class="line">            |-- config</div><div class="line">            |-- index.ts</div><div class="line">            |-- module-name.module.ts</div><div class="line">    |-- main.ts</div><div class="line">    |-- main.module.ts</div></pre></td></tr></table></figure>
<h3 id="配置管理"><a href="#配置管理" class="headerlink" title="配置管理"></a>配置管理</h3><p>我目前初步的想法是通过提供一个 <code>ConfigModule</code> 暴露出一个 <code>ConfigService</code> 来提供配置的获取和查看。</p>
<p>在某些情况下，可能需要多级配置，模块级别的配置，应用级别的配置。那么 <code>ConfigService</code> 可以在获取配置的时候自动合并这些规则。</p>
<h3 id="进程管理"><a href="#进程管理" class="headerlink" title="进程管理"></a>进程管理</h3><p>现在已经是 18 年了，不用 Docker 你真的对得起自己么？很明显是对不起的。所以进程管理这一块，我们就交给 Docker 来处理。包括启动、停止、重启、日志等，都交给 Docker。</p>
<p>于是启动命令就可以简化成 <code>node dest/main.js</code> 即可。</p>
<p>那么你可能会想到，如果一个 Docker 环境给你分配了两个 u，那岂不是会浪费一个 u。理论上是的，那么你就可以通过 pm2 啊啥的自己去管理吧，哈哈哈，不管。</p>
<h2 id="Iron-js"><a href="#Iron-js" class="headerlink" title="Iron.js"></a>Iron.js</h2><p>说了这么多，把上面的内容都沉淀下来，我得要给他取个名字，于是我就取成了 Iron。为啥叫 Iron 呢？因为 Iron Man。那为啥因为 Iron Man 呢？因为他制作的盔甲可以自由拆分，自动拼合。非常适合我们这个项目的形态。</p>
<p>不过这个项目什么时候能沉淀下来，看我心情了。不过定个时间线吧，就在 4 月底，争取搞定。</p>
<p>因为这里面最大的问题就是配置的问题，需要深入依赖注入，所以会麻烦一些。但是其他的方面更多的只是一种约束吧。</p>
<p>这就是我用 Nest.js 一周以来的心得。暂时就想到这么多，更多的内容等我后面再分析吧。</p>
<p>写完睡觉，答应女票了，啦啦啦~</p>

                    
                        

                    
                    
                        <p>
                            <a href="/2018/04/16/nest-js-try-first/#post-footer" class="postShorten-excerpt_link link">
                                注释和共享
                            </a>
                        </p>
                    
                </div>
            
        </div>
        
    </article>
    
    
    <article class="postShorten postShorten--thumbnailimg-bottom" itemscope itemType="http://schema.org/BlogPosting">
        <div class="postShorten-wrap">
            <div class="postShorten-header">
                <h1 class="postShorten-title" itemprop="headline">
                    
                        <a class="link-unstyled" href="/2018/02/16/summary-2018/">
                            2018 年终总结
                        </a>
                    
                </h1>
                <div class="postShorten-meta">
    <time itemprop="datePublished" content="Fri Feb 16 2018 00:04:44 GMT+0800">
	
		    2月 16, 2018
    	
    </time>
    
        <span>发布在 </span>
        
    <a class="category-link" href="/categories/年终总结/">年终总结</a>


    
</div>
            </div>
            
            
                <div class="postShorten-content" itemprop="articleBody">
                    <p>2017 年，有开心，也有失落。也不知道该从何说起，就随便写写了。高中语文就没学好过，所以可能写成流水账了。请各位看官多多包涵。</p>
<h2 id="实习"><a href="#实习" class="headerlink" title="实习"></a>实习</h2><p>如今步入大四，最大的心事就是找实习找 Offer 了。先后经历了两次阿里面试的失利，至于失利的过程，我之前有吐槽过，就不多说了，而且听说今年的面试难度提升到了社招，心痛。饿了么/七牛都有投递过，但是都没有进。如果各位看官想招 Node.js 或者 前端 的校招生的话，请联系我哦~ <code>请在各大社交媒体请搜索 XGHeaven 即可</code></p>
<p>终于在 CNode 上面找到了一家美国 AR 公司，Integem。</p>
<p>在里面主要是用 Electron 做客户端，技术栈就是 Vue 全家桶，不多说了。反正整体干下来的感觉其实和自己接了一个外包的感觉差不多，而且设计师设计的页面参差不齐。最可气的是，假设页面 A 和页面 B 相同的部分，没准一个就有边框，另一个就没有。没准一个字体是 12px，另一个可能就是 16px。真是受不了，一开始，我还是让他确认一下到底哪个设计图是对的，到最后，只能用我的佛系心态对待这个设计图，懒得问了。切个图，也是切的乱七八糟，我还是自己来好了。</p>
<p>在里面差不多干了 5 个月，再加上学校要求每个人都要去跟着导师完成一个实践项目，于是我就离开了。</p>
<p>再之后，我在逛实习的时候，突然发现大搜车在招 Node.js 实习生，而且标注的是有大牛带。大搜车……为啥听着这么耳熟啊，不管了，报了再说。于是我就去面试了。面试我的是一个胖胖的留着胡子的人（死月罪过，当时并不知道那是你），于是和他相聊甚欢，最后还记得，当时看到他的 15寸 macbook pro 后面贴着一个 bad apple 的一个贴纸。当时顺口就问了一句，这个是 bad apple 么？当时之所以会问这个，因为我还记得很早以前，看过一个人的博客，里面讲到了他在花瓣网工作，而且很详细的讲解了图片主题色的提取和 Node.js NAN API，感觉受益匪浅，于是我当时在想，这是哪个学校的大牛，竟然这么厉害。后来看到他已经在花瓣工作了。。。不过我记得他很喜欢二次元。。。于是面试的时候，我在想会不会是他，于是就问了句贴纸，以为他会聊起二次元，结果他随便应了一句就过去了。我见状就没再继续问下去了。后面安慰道自己说，没事，那个人应该不会在大搜车。面试我这个人感觉还是很厉害的~~至少有一点，我说我提过 issue 给 node，他能马上就打开 github 看。这一点让我很敬佩，因为大部分面试都是会听你描述，看简历上面写的，而不是当场去查看。举个例子，你跟面试官说你博客写了很多高品质的文章，大部分面试官会直接问你有什么，他不会自己去看。而好的面试官会一边问你，一边自己打开看。。。我是这么觉得的。反正不管当时是不是月老，我已经决定来这家公司了。</p>
<p>哈哈哈，后面等进大搜车之后，剧情反转。那个人其实就是死月。当时看到他在 QQ 群里面的时候，你知道我的心里有多么开心么！！但是，有一个噩耗，就是他在我去的前几周已经跳槽去蚂蚁金服了，哭 (((T____T))) 我的大牛啊，你怎么走了啊~啊~啊~啊~</p>
<p>不过还好，也认识了挺多大牛的，不过还是没见到过我心仪的 朴灵/不四/狼叔 -_-。</p>
<p>现在在大搜车呆了有两个月了吧，那就简单总结一下干了啥吧：</p>
<ul>
<li>完成了一个图片上传服务，里面包含了公共的图片上传，以及大风车的头像上传，真正的编码时间也就两个星期，但是真的发布上去，却花了一个月。</li>
<li>现在准备一个请求限制框架，讲道理这是很简单的一个工作，但是我看时间很充裕，于是我就想写大，看看能不能独立成一个库，开源骗 star。</li>
<li>期间还要各种小东西，修修补补。</li>
</ul>
<p>刚进大搜车，按理来说，带我的人应该是小山，但是那个时候他请了几天假，于是就鹏飞暂时带着我。让我看了 Akyuu.js 和帕秋莉网关。之后其实所有的时间都是跟着鹏飞，我师父小山感觉不喜欢多说话，平时也没有太多的共同语言。想平时打打游戏联络一下感情，但是看他很忙的样子，就放弃了。最近才发现，原来小山也看二次元，哈哈哈。反正就这样，和小山半亲近半陌生。和鹏飞一开始也聊的挺多，后面等公司的事情知道的差不多了，也交流的不多了。</p>
<p>而且由于我比较慢热，再加上我进入公司比较晚，没有参加过团建，和大家都不是很熟。就和组内的坐在旁边的外加组内的实习生比较熟。</p>
<p>说一下，我在大搜车实习的感受吧。</p>
<ul>
<li>代码层面<ul>
<li>更加理性的对待 callback 和 promise，因为在之前，我是极力反对使用 callback 的，所以当第一眼看到公司的代码的时候，我懵逼了，怎么全是 callback。于是经过和鹏飞的交流以及自己的领悟，终于放下执念。其实 callback+async 和 promise 没啥区别么，哈哈哈。</li>
<li>尝试先写文档，后写代码。我平时兴起的时候，直接就开始撸，从来不打草稿。小项目可以，但是当项目大了之后，就呵呵哒了。</li>
<li>了解了 Node 的 PR 流程。</li>
<li>其他的好像还真的没有了，什么代码规范，git flow，框架的使用等等，我基本都了解。但是又重新复习了并精进了一下。毕竟之前看时候只是看了几眼，大体明白了内容。正好趁着这次实习，运用一下，看看自己理解的哪里有问题。</li>
</ul>
</li>
<li>交际层面<ul>
<li>首先我是一个慢热的，也就是说我不是很擅长去找别人交流，但是别人来找我交流，我是很乐意的。所以说，我当初进入公司之前的幻想，就是大家都在交流着各种新技术，新框架，新事物，当一个人抛出问题的时候，大家会一起去解决研究。结果进来之后，我发现，好像群里半天都不会有任何消息。于是我就努力去带动气氛，有什么好玩的东东都尝试发到群里，结果还是很难带起氛围。不知道是大家太忙了，还是我发的信息太简单。</li>
<li>还记得在学校的时候，社团的技术群，可能一个人发现了新东西，于是群里的大佬一起去尝试，评论，总结。和群里的人撕逼那个语言好，撕逼什么框架好用。我们只追求方便好用，并不怎么在乎稳定性这种东西。哎，这种感觉好难在找回来了。不知道头哥能不能看到我写的，我不知道头哥你想的团队是不是我想的那样，但是感觉大家仅仅为了业务而工作，死气沉沉，不觉得失去了乐趣么？</li>
<li>再讲讲开源的东东，我不知道用我们大学生的思维来思考对不对。至少我会很讨厌所有的公司沉淀出来的产品，比如说阿里的 egg。怎么解释呢？你可以理解假设公司内部的开发版本按照 master 的一条线进行，如果开源了，我就从 master checkout 一个新的分支，然后做一些开源的修改。我为什么不喜欢这种呢？因为这种所有的功能的设计实现就是严重依赖业务的，他只能做到的是在这个业务的情况下尽可能去兼容其他的业务格式而抽离出来的核心。而我真正想要的是什么呢？是社区驱动，一个产品可以由一个公司来开发，但是设计一定还给社区。而且我也不喜欢所谓的二次封装的框架，二次封装的框架我建议内部使用，而不是开源。除非你的二次封装能够提供很多功能。</li>
</ul>
</li>
<li>反正总的来讲，我感觉从知识层面，我获取的很少，没获取到太多新的概念/知识。但是实践层面我获取的还是挺多的。不知道这是不是以后工作的常态。</li>
</ul>
<h2 id="大学"><a href="#大学" class="headerlink" title="大学"></a>大学</h2><p>这一年其实大学生活没有太多的东西，主要是在实习当中度过的。</p>
<p>正是因为如此，我也越发怀念当初的学习生活。回头看看学弟当中的大佬，不由自主的感叹自己好像虚度了大学生活。</p>
<p>不顾了，下学期就准备毕业设计了，希望自己能珍惜最后的时光吧。</p>
<h2 id="2018-展望"><a href="#2018-展望" class="headerlink" title="2018 展望"></a>2018 展望</h2><p>看过了死月的总结，我发现其实有一点挺好的，一年给自己定一些目标，来年看看目标有没有实现。</p>
<p>那我也来展望一下好了：</p>
<ul>
<li>学习<ul>
<li>争取研读 Node 源码，至少要把死月那本书给啃完</li>
<li>争取 Github 每周都有贡献，希望能长久的维护一个项目</li>
<li>争取写一些有意思，有难度的代码，比如说《如何写出一个 Babel》</li>
<li>坚持写文章吧（这个有点难），至少保证每两周一篇高质量的</li>
<li>了解一些其他方面的内容，下一年总结一下了解了啥。</li>
</ul>
</li>
<li>坚持锻炼身体，把体重控制在 65 以下，争取练出胸肌（其实我有，只不过有点萎缩了）腹肌二头肌（当然，这些都练出来之后我就不限制体重了）。</li>
<li>争取学会做几个菜（当然指的是在实习期间了），暂时只考虑用电饭煲来做。</li>
<li>Minecraft 开新坑，等 1.13 发布~如果有小伙伴想入坑的请联系我，最好有正版，因为我是想单人开坑的。</li>
<li>不知道为啥，自从实习之后，感觉有点思春。。。特别想找人聊聊非技术方面的事情。。没妹子陪，我都不想去电影院看电影了。。。所以，不求找女票，感觉自己现在还不是很适合去当男票，其实是找不到合适的，哈哈哈。只求可以找到有空可以出去看个电影，聊个天，而且臭味相投的妹子就好了。不过目测是完不成这个目标了。</li>
<li>既然没有女票，那就多花一些时间在学习上。但是不要死学习，做程序员路上的书呆子。</li>
<li>恶习<ul>
<li>争取改掉拖沓的毛病，有任务赶紧去做，有事情提前安排</li>
<li>争取每天刷牙洗脸（也就是让自己早起，哈哈哈）</li>
</ul>
</li>
</ul>
<p>先这么多吧，看看 2019 年，我完成了哪些。</p>

                    
                        

                    
                    
                        <p>
                            <a href="/2018/02/16/summary-2018/#post-footer" class="postShorten-excerpt_link link">
                                注释和共享
                            </a>
                        </p>
                    
                </div>
            
        </div>
        
    </article>
    
    <div class="pagination-bar">
    <ul class="pagination">
        
        
        <li class="pagination-number">第 1 页 共 1 页</li>
    </ul>
</div>

</section>



                <footer id="footer" class="main-content-wrap">
    <span class="copyrights">
        Copyrights &copy; 2018 XGHeaven. All Rights Reserved.
    </span>
</footer>

            </div>
            
        </div>
        


    

<div id="about">
    <div id="about-card">
        <div id="about-btn-close">
            <i class="fa fa-remove"></i>
        </div>
        
            <img id="about-card-picture" src="https://www.gravatar.com/avatar/f34474afe04544ffbe944a804f0ae762?s=110"/>
        
            <h4 id="about-card-name">XGHeaven</h4>
        
            <h5 id="about-card-bio"><p>一个弱弱的码农</p>
</h5>
        
        
            <h5 id="about-card-job">
                <i class="fa fa-briefcase"></i>
                <br/>
                <p>杭州电子科技大学学生一枚</p>

            </h5>
        
        
            <h5 id="about-card-location">
                <i class="fa fa-map-marker"></i>
                <br/>
                Weifang Shandong, China
            </h5>
        
    </div>
</div>

        
<div id="cover" style="background-image:url('/static/images/20170217-TombRaider.jpg');"></div>
    </body>
    <!--SCRIPTS-->
<script src="/assets/js/scrip-z6xcdnzggiy56kzp83ux5nnbwra1acrauxruz3kdi3u5xladb6jh4n3ylebm.min.js"></script>
<!--SCRIPTS END-->



</html>
